#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/platform_device.h>

struct input_dev *vms_input_dev;            /* 定义一个输入设备，定义设备属性 */
static struct platform_device *vms_dev;     /* 设备结构体，用来产生接口文件 */

/* sysfs节点操作方法，用来向虚拟鼠标驱动输入模拟坐标 
 * 将输入设备 和 接口文件 联系起来
 * 被传递的函数参数是文件接口，实际被操作的是输入设备
 */

static ssize_t write_vms(struct device *dev,
                         struct device_attribute *attr,
                         const char *buffer, size_t count)
{
    int x, y;
    sscanf(buffer, "%d%d", &x, &y);
    /* 
     * 通过event接口(evdev)传递相对坐标，
     * 并且传递顺序与下面的vms_init函数中的读取顺序相同，
     * 先x后y
     */
    input_report_rel(vms_input_dev, REL_X, x);
    input_report_rel(vms_input_dev, REL_Y, y); 
    printk("%d %d", x, y);

    /* 
     * 一次动作执行完后，执行下面的语句后，
     * input子系统将上面两次event数据打包成一个
     * event包，发送到/dev/input/eventX
     */
    input_sync(vms_input_dev);
    return count;
}

/* 
 * 以下主要为创建sysfs中vms目录以及coordinates接口的设置
 */

/* 
 * 绑定sysfs写方法，参数的含义分别为sysfs中接口名称，
 * 读写权限，读函数，写函数 
 */

DEVICE_ATTR(coordinates, 0644, NULL, write_vms);

/* vms组的 attribute描述符 */
static struct attribute *vms_attrs[]= 
{
    &dev_attr_coordinates.attr,     /* 该结构体上面的DEVICE_ATTR产生的 */
    NULL
};
/* vms attribute组 */
static struct attribute_group vms_attr_group =
{
    .attrs = vms_attrs,
};


/* 驱动初始化 */
int __init vms_init(void)
{
    /* 以下代码主要为创建sysfs中的接口作准备 */
    /* 注册一个平台设备(platform device),在platform下产生一个vms目录 */
    vms_dev = platform_device_register_simple("vms", -1, NULL, 0);
    if(IS_ERR(vms_dev))
    {
        PTR_ERR(vms_dev);
        printk("vms_init:error \n");
    }

    /* 创建一个sysfs中vms目录下一个coordinates结点用来读取虚拟坐标值 */
    sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group);



    /* 创建虚拟设备 */
    vms_input_dev = input_allocate_device();
    if(!vms_input_dev)
    {
        printk("Bad input_alloc_device()\n");
    }

    /* 注意：虚拟鼠标将产生的是相对坐标值 */
    set_bit(EV_REL, vms_input_dev->evbit);      /* 设备接收的事件类型：相对事件 */
    set_bit(REL_X, vms_input_dev->relbit);      /* 事件：X位置相对坐标 */
    set_bit(REL_Y, vms_input_dev->relbit);      /* 事件：Y位置相对坐标 */

    /* 向input子系统注册设备 */
    input_register_device(vms_input_dev);
    printk("Virtual Mouse Driver Initialized. \n");

    return 0;
}

/* 驱动退出函数 */
void vms_cleanup(void)
{
    /* 取消input子系统的登记 */
    input_unregister_device(vms_input_dev);

    /* 清除sysfs结点 */
    sysfs_remove_group(&vms_dev->dev.kobj, &vms_attr_group);

    /* 取消驱动的注册 */
    platform_device_unregister(vms_dev);
    return;
}

module_init(vms_init);
module_exit(vms_cleanup);
MODULE_LICENSE("GPL"); /* 缺这句的话会加载驱动时，会出现unknown symbol的错误 */
